﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace VersionComparer.Base
{
    /// <summary>
    /// This class manages add ins
    /// </summary>
    public class AddInsManager
    {
        #region fields
        /// <summary>
        /// list of known assemblies
        /// </summary>
        Assembly[] assemblies;
        /// <summary>
        /// synchronization object
        /// </summary>
        object syncObject = new object ();
        #endregion fields

        #region statics
        /// <summary>
        /// Current manager
        /// </summary>
        public static AddInsManager current = new AddInsManager();
        /// <summary>
        /// Current manager
        /// </summary>
        public static AddInsManager Current
        {
            get{
            return current;
            }
        }
        #endregion statics

        #region constructors
        private AddInsManager()
        {
        }
        #endregion constructors

        #region properties
        /// <summary>
        /// Returns the list of assemblies
        /// </summary>
        private Assembly[] Assemblies
        {
            get
            {
                lock ( syncObject )
                {
                    if ( assemblies == null )
                        assemblies = LoadAssemblies ();
                    return assemblies;
                }
            }
        }
        #endregion properties

        /// <summary>
        /// Loads assemblies from the same folder as the current executable
        /// </summary>
        /// <returns>list of known assemblies</returns>
        private Assembly[] LoadAssemblies()
        {
            List<Assembly> result = new List<Assembly>();
            string path = Path.GetDirectoryName(GetType().Assembly.Location);
            List<string> files = new List<string>();
            files.AddRange(Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly));
            files.AddRange(Directory.GetFiles(path, "*.exe", SearchOption.TopDirectoryOnly));
            foreach (string file in files)
            {
                try
                {
                    var assembly = System.Reflection.Assembly.LoadFrom(file);
                    result.Add(assembly);
                }
                catch
                {
                }
            }
            return result.ToArray();
        }

        /// <summary>
        /// Checks all the assemblies in the same folder for all the non-abstract types that inherit from T. It creates an instance of each type and returns all of them in a list.
        /// </summary>
        /// <typeparam name="T">type of objects to return</typeparam>
        /// <returns></returns>
        public T[] GetObjectInstancesOfType<T>()
            where T: class
        {
            List<T> result = new List<T> ();
            foreach (Type type in GetObjectsOfType<T>())
            {
                try
                {
                    result.Add(Activator.CreateInstance(type) as T);
                }
                catch
                {
                }
            }
            return result.ToArray();
        }

        /// <summary>
        /// Searches the loaded assemblies for non-abstract types that inherit from T
        /// </summary>
        /// <typeparam name="T">type of objects to search</typeparam>
        /// <returns>list of non abstract types that inherit from T</returns>
        public Type[] GetObjectsOfType<T>()
            where T: class
        {
            List<Type> result = new List<Type>();
            Type baseType = typeof(T);
            foreach (var assembly in Assemblies)
            {
                foreach (Type type in assembly.GetTypes())
                    if (type.IsSubclassOf(baseType) && (!type.IsAbstract))
                        result.Add(type);
            }
            return result.ToArray ();
        }
    }
}
